{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "whjsJasuhstV"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/jeffheaton/app_generative_ai/blob/main/t81_559_class_07_1_agents.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "euOZxlIMhstX"
   },
   "source": [
    "# T81-559: Applications of Generative Artificial Intelligence\n",
    "**Module 7: LangChain: Agents**\n",
    "* Instructor: [Jeff Heaton](https://sites.wustl.edu/jeffheaton/), McKelvey School of Engineering, [Washington University in St. Louis](https://engineering.wustl.edu/Programs/Pages/default.aspx)\n",
    "* For more information visit the [class website](https://sites.wustl.edu/jeffheaton/t81-558/)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "d4Yov72PhstY"
   },
   "source": [
    "# Module 7 Material\n",
    "\n",
    "* **Part 7.1: Introduction to LangChain Agents** [[Video]](https://www.youtube.com/watch?v=J5Vr___lSSs) [[Notebook]](t81_559_class_07_1_agents.ipynb)\n",
    "* Part 7.2: Understanding LangChain Agent Tools [[Video]](https://www.youtube.com/watch?v=qMquBmteYw4) [[Notebook]](t81_559_class_07_2_tools.ipynb)\n",
    "* Part 7.3: LangChain Retrival and Search Tools [[Video]](https://www.youtube.com/watch?v=NB5qGPLoBBE) [[Notebook]](t81_559_class_07_3_search_tools.ipynb)\n",
    "* Part 7.4: Constructing LangChain Agents [[Video]](https://www.youtube.com/watch?v=OJe5oHvrdHk) [[Notebook]](t81_559_class_07_4_more_agent.ipynb)\n",
    "* Part 7.5: Custom Agents [[Video]](https://www.youtube.com/watch?v=IsJemVYSEdc) [[Notebook]](t81_559_class_07_5_custom_agent.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "AcAUP0c3hstY"
   },
   "source": [
    "# Google CoLab Instructions\n",
    "\n",
    "The following code ensures that Google CoLab is running and maps Google Drive if needed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "xsI496h5hstZ",
    "outputId": "3fa5f9a2-9e3e-4830-b30f-398ccfd9e837"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Note: using Google CoLab\n",
      "Collecting langchain\n",
      "  Downloading langchain-0.2.6-py3-none-any.whl (975 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m975.5/975.5 kB\u001b[0m \u001b[31m4.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting langchain_openai\n",
      "  Downloading langchain_openai-0.1.11-py3-none-any.whl (40 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m40.8/40.8 kB\u001b[0m \u001b[31m2.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting langchain_experimental\n",
      "  Downloading langchain_experimental-0.0.62-py3-none-any.whl (202 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m202.7/202.7 kB\u001b[0m \u001b[31m10.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting duckduckgo-search\n",
      "  Downloading duckduckgo_search-6.1.7-py3-none-any.whl (24 kB)\n",
      "Collecting langchainhub\n",
      "  Downloading langchainhub-0.1.20-py3-none-any.whl (5.0 kB)\n",
      "Requirement already satisfied: PyYAML>=5.3 in /usr/local/lib/python3.10/dist-packages (from langchain) (6.0.1)\n",
      "Requirement already satisfied: SQLAlchemy<3,>=1.4 in /usr/local/lib/python3.10/dist-packages (from langchain) (2.0.31)\n",
      "Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in /usr/local/lib/python3.10/dist-packages (from langchain) (3.9.5)\n",
      "Requirement already satisfied: async-timeout<5.0.0,>=4.0.0 in /usr/local/lib/python3.10/dist-packages (from langchain) (4.0.3)\n",
      "Collecting langchain-core<0.3.0,>=0.2.10 (from langchain)\n",
      "  Downloading langchain_core-0.2.10-py3-none-any.whl (332 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m332.8/332.8 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting langchain-text-splitters<0.3.0,>=0.2.0 (from langchain)\n",
      "  Downloading langchain_text_splitters-0.2.2-py3-none-any.whl (25 kB)\n",
      "Collecting langsmith<0.2.0,>=0.1.17 (from langchain)\n",
      "  Downloading langsmith-0.1.82-py3-none-any.whl (127 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m127.4/127.4 kB\u001b[0m \u001b[31m6.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: numpy<2,>=1 in /usr/local/lib/python3.10/dist-packages (from langchain) (1.25.2)\n",
      "Requirement already satisfied: pydantic<3,>=1 in /usr/local/lib/python3.10/dist-packages (from langchain) (2.7.4)\n",
      "Requirement already satisfied: requests<3,>=2 in /usr/local/lib/python3.10/dist-packages (from langchain) (2.31.0)\n",
      "Requirement already satisfied: tenacity!=8.4.0,<9.0.0,>=8.1.0 in /usr/local/lib/python3.10/dist-packages (from langchain) (8.4.1)\n",
      "Collecting openai<2.0.0,>=1.32.0 (from langchain_openai)\n",
      "  Downloading openai-1.35.7-py3-none-any.whl (327 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m327.5/327.5 kB\u001b[0m \u001b[31m22.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting tiktoken<1,>=0.7 (from langchain_openai)\n",
      "  Downloading tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m28.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting langchain-community<0.3.0,>=0.2.6 (from langchain_experimental)\n",
      "  Downloading langchain_community-0.2.6-py3-none-any.whl (2.2 MB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.2/2.2 MB\u001b[0m \u001b[31m29.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: click>=8.1.7 in /usr/local/lib/python3.10/dist-packages (from duckduckgo-search) (8.1.7)\n",
      "Collecting pyreqwest-impersonate>=0.4.8 (from duckduckgo-search)\n",
      "  Downloading pyreqwest_impersonate-0.4.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.7 MB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.7/2.7 MB\u001b[0m \u001b[31m45.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting orjson>=3.10.5 (from duckduckgo-search)\n",
      "  Downloading orjson-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (144 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m145.0/145.0 kB\u001b[0m \u001b[31m4.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: packaging<25,>=23.2 in /usr/local/lib/python3.10/dist-packages (from langchainhub) (24.1)\n",
      "Collecting types-requests<3.0.0.0,>=2.31.0.2 (from langchainhub)\n",
      "  Downloading types_requests-2.32.0.20240622-py3-none-any.whl (15 kB)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.1)\n",
      "Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (23.2.0)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.4.1)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.5)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.9.4)\n",
      "Collecting dataclasses-json<0.7,>=0.5.7 (from langchain-community<0.3.0,>=0.2.6->langchain_experimental)\n",
      "  Downloading dataclasses_json-0.6.7-py3-none-any.whl (28 kB)\n",
      "Collecting jsonpatch<2.0,>=1.33 (from langchain-core<0.3.0,>=0.2.10->langchain)\n",
      "  Downloading jsonpatch-1.33-py2.py3-none-any.whl (12 kB)\n",
      "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai<2.0.0,>=1.32.0->langchain_openai) (3.7.1)\n",
      "Requirement already satisfied: distro<2,>=1.7.0 in /usr/lib/python3/dist-packages (from openai<2.0.0,>=1.32.0->langchain_openai) (1.7.0)\n",
      "Collecting httpx<1,>=0.23.0 (from openai<2.0.0,>=1.32.0->langchain_openai)\n",
      "  Downloading httpx-0.27.0-py3-none-any.whl (75 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.6/75.6 kB\u001b[0m \u001b[31m6.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai<2.0.0,>=1.32.0->langchain_openai) (1.3.1)\n",
      "Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai<2.0.0,>=1.32.0->langchain_openai) (4.66.4)\n",
      "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from openai<2.0.0,>=1.32.0->langchain_openai) (4.12.2)\n",
      "Requirement already satisfied: annotated-types>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1->langchain) (0.7.0)\n",
      "Requirement already satisfied: pydantic-core==2.18.4 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1->langchain) (2.18.4)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2->langchain) (3.3.2)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2->langchain) (3.7)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2->langchain) (2.0.7)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2->langchain) (2024.6.2)\n",
      "Requirement already satisfied: greenlet!=0.4.17 in /usr/local/lib/python3.10/dist-packages (from SQLAlchemy<3,>=1.4->langchain) (3.0.3)\n",
      "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.10/dist-packages (from tiktoken<1,>=0.7->langchain_openai) (2024.5.15)\n",
      "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai<2.0.0,>=1.32.0->langchain_openai) (1.2.1)\n",
      "Collecting marshmallow<4.0.0,>=3.18.0 (from dataclasses-json<0.7,>=0.5.7->langchain-community<0.3.0,>=0.2.6->langchain_experimental)\n",
      "  Downloading marshmallow-3.21.3-py3-none-any.whl (49 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.2/49.2 kB\u001b[0m \u001b[31m4.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting typing-inspect<1,>=0.4.0 (from dataclasses-json<0.7,>=0.5.7->langchain-community<0.3.0,>=0.2.6->langchain_experimental)\n",
      "  Downloading typing_inspect-0.9.0-py3-none-any.whl (8.8 kB)\n",
      "Collecting httpcore==1.* (from httpx<1,>=0.23.0->openai<2.0.0,>=1.32.0->langchain_openai)\n",
      "  Downloading httpcore-1.0.5-py3-none-any.whl (77 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m77.9/77.9 kB\u001b[0m \u001b[31m2.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting h11<0.15,>=0.13 (from httpcore==1.*->httpx<1,>=0.23.0->openai<2.0.0,>=1.32.0->langchain_openai)\n",
      "  Downloading h11-0.14.0-py3-none-any.whl (58 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m6.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting jsonpointer>=1.9 (from jsonpatch<2.0,>=1.33->langchain-core<0.3.0,>=0.2.10->langchain)\n",
      "  Downloading jsonpointer-3.0.0-py2.py3-none-any.whl (7.6 kB)\n",
      "Collecting mypy-extensions>=0.3.0 (from typing-inspect<1,>=0.4.0->dataclasses-json<0.7,>=0.5.7->langchain-community<0.3.0,>=0.2.6->langchain_experimental)\n",
      "  Downloading mypy_extensions-1.0.0-py3-none-any.whl (4.7 kB)\n",
      "Installing collected packages: types-requests, pyreqwest-impersonate, orjson, mypy-extensions, marshmallow, jsonpointer, h11, typing-inspect, tiktoken, langchainhub, jsonpatch, httpcore, duckduckgo-search, langsmith, httpx, dataclasses-json, openai, langchain-core, langchain-text-splitters, langchain_openai, langchain, langchain-community, langchain_experimental\n",
      "Successfully installed dataclasses-json-0.6.7 duckduckgo-search-6.1.7 h11-0.14.0 httpcore-1.0.5 httpx-0.27.0 jsonpatch-1.33 jsonpointer-3.0.0 langchain-0.2.6 langchain-community-0.2.6 langchain-core-0.2.10 langchain-text-splitters-0.2.2 langchain_experimental-0.0.62 langchain_openai-0.1.11 langchainhub-0.1.20 langsmith-0.1.82 marshmallow-3.21.3 mypy-extensions-1.0.0 openai-1.35.7 orjson-3.10.5 pyreqwest-impersonate-0.4.8 tiktoken-0.7.0 types-requests-2.32.0.20240622 typing-inspect-0.9.0\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "\n",
    "try:\n",
    "    from google.colab import drive, userdata\n",
    "    COLAB = True\n",
    "    print(\"Note: using Google CoLab\")\n",
    "except:\n",
    "    print(\"Note: not using Google CoLab\")\n",
    "    COLAB = False\n",
    "\n",
    "# OpenAI Secrets\n",
    "if COLAB:\n",
    "    os.environ[\"OPENAI_API_KEY\"] = userdata.get('OPENAI_API_KEY')\n",
    "\n",
    "# Install needed libraries in CoLab\n",
    "if COLAB:\n",
    "    !pip install langchain langchain_openai langchain_experimental duckduckgo-search langchainhub"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "pC9A-LaYhsta"
   },
   "source": [
    "# 7.1: LangChain Agents\n",
    "\n",
    "LangChain allows the use of [agents](https://python.langchain.com/v0.1/docs/modules/agents/), which revolutionize the way we interact with language models by transforming them into dynamic decision-makers. Unlike traditional chains, where each step is meticulously programmed, agents leverage the inherent reasoning capabilities of language models to choose their own paths. Imagine an intelligent system that not only understands the context but also determines the most effective sequence of actions to achieve a goal. This adaptability marks a significant departure from rigid, pre-defined workflows. By acting as a reasoning engine, the language model assesses each situation and decides which actions to take and in what order, offering unparalleled flexibility and efficiency. This chapter delves into the fascinating world of lang-chain agents, exploring how they harness the power of language models to autonomously navigate complex tasks, adapt to changing conditions, and deliver sophisticated solutions with minimal human intervention.\n",
    "\n",
    "LangChain suppors multiple [agent types](https://python.langchain.com/v0.1/docs/modules/agents/agent_types/). You will interface with these agents via two different methods:\n",
    "\n",
    "* **Chat Models**: Designed to take in messages and output messages, requiring a specific prompting strategy for optimal performance.\n",
    "* **LLMs**: Intended to take in a string and output a string, using a different prompting strategy to achieve the best results.\n",
    "\n",
    "Some of the other important features for agent types include:\n",
    "\n",
    "* **Supports Chat History**: Indicates if an agent can function as a chatbot, typically requiring advanced models.\n",
    "* **Supports Multi-Input Tools**: Indicates if an agent can use tools requiring multiple inputs, often unsupported by earlier models.\n",
    "* **Supports Parallel Function Calling**: Indicates if an agent can call multiple tools simultaneously, requiring more advanced models.\n",
    "\n",
    "The LangChain agent types can be summarized as follows:\n",
    "\n",
    "|Agent Type|Interface|Chat<br>History|Multi-Input<br>Tools|Parallel<br>Function Calling|When to use|\n",
    "|--|--|--|--|--|--|\n",
    "|Tool Calling|chat|✅|✅|✅|Used in this course, also tool-calling agents.\n",
    "|XML|LLM|✅|||Use with XML models, such as Anthropic\n",
    "|Structured Chat|chat|✅|✅||If you need to support tools with multiple inputs.\n",
    "|JSON Chat|chat|✅|||If you are using a model good at JSON|\n",
    "|ReAct|LLM|✅|||Complex reasoning|\n",
    "|Self-Ask w/Search|LLM||||If you are using a simple model and only have one search tool|\n",
    "\n",
    "\n",
    "In this course, we will focus primarily on tool-calling agents, which are designed to interact with external tools and APIs to perform specific tasks, such as data retrieval, processing, or executing actions based on user inputs. Tool-calling agents are highly versatile and are essential for building applications that require integration with various external systems. Besides tool-calling agents, there are two other important types: ReAct agents and Self-Ask with Search agents. ReAct agents combine reasoning and acting capabilities, making them suitable for complex decision-making and task automation scenarios where the agent needs to logically process information and take appropriate actions. Self-Ask with Search agents, on the other hand, are designed to enhance the language model's capabilities by integrating search functionalities, allowing the agent to retrieve up-to-date information from external sources. This makes them ideal for applications requiring accurate and current data retrieval, such as news aggregation, fact-checking, and research tools. Each type of agent serves a unique purpose, and understanding their use cases will enable you to select the appropriate agent for your specific application needs. We will also cover how to create an entirely custom agent.\n",
    "\n",
    "## Key Components of Agents\n",
    "\n",
    "Agents use a language model to decide on a sequence of actions. Unlike chains, where actions are predefined in the code, agents utilize a language model as a reasoning engine to determine the actions and their order.\n",
    "\n",
    "* **AgentAction**: An AgentAction is a dataclass that defines the action an agent should take. It includes a tool property, specifying the name of the tool to be invoked, and a tool_input property, providing the input for that tool.\n",
    "\n",
    "* **AgentFinish**: AgentFinish signifies the final result from an agent, ready to be returned to the user. It contains a return_values key-value mapping, typically holding an output key with a string representing the agent's response.\n",
    "\n",
    "* **Intermediate Steps**: Intermediate steps represent previous agent actions and their corresponding outputs during the current run. These steps are crucial for informing future iterations, allowing the agent to track completed tasks. This is typed as a List (Tuple[AgentAction, Any), with observation being flexible and often a string.\n",
    "\n",
    "## Agent Chain Structure\n",
    "\n",
    "The agent is responsible for deciding the next step, usually powered by a language model, a prompt, and an output parser. Different agents use various prompting styles, input encoding methods, and output parsing techniques. For a comprehensive list of built-in agents, refer to the agent types section. Custom agents can also be created for more control.\n",
    "\n",
    "* **Agent Inputs**: Inputs to an agent are key-value mappings, with intermediate_steps being the only required key, corresponding to the intermediate steps mentioned earlier. The PromptTemplate typically transforms these pairs into a format suitable for the LLM.\n",
    "\n",
    "* **Agent Outputs**: The outputs can be the next action(s) to take or the final response to send to the user (AgentActions or AgentFinish). Concretely, this can be typed as Union (AgentAction, List[AgentAction], AgentFinish). The output parser converts the raw LLM output into one of these types.\n",
    "\n",
    "## The AgentExecutor\n",
    "The agent executor is the runtime that calls the agent, executes chosen actions, passes the action outputs back to the agent, and repeats the process. This runtime handles complexities such as:\n",
    "\n",
    "* Selecting non-existent tools\n",
    "* Managing tool errors\n",
    "* Parsing tool invocation outputs\n",
    "* Logging and observability at all levels (agent decisions, tool calls)\n",
    "\n",
    "## Agent Tools\n",
    "Tools are functions that an agent can invoke. The Tool abstraction consists of two components:\n",
    "\n",
    "The input schema for the tool, informing the LLM of the necessary parameters.\n",
    "The function to run, typically a Python function.\n",
    "\n",
    "## Agent Toolkits\n",
    "For common tasks, agents often require a set of related tools. LangChain provides toolkits—groups of 3-5 tools for specific objectives. For example, the GitHub toolkit includes tools for searching issues, reading files, and commenting. LangChain offers various toolkits to get started. For a full list, see the toolkits integrations section.\n",
    "\n",
    "## Basic Agent Exmaple\n",
    "\n",
    "Let's now take a look at a basic agent. An agent in this context is a system designed to perform specific tasks by leveraging tools and models. At a high level, this agent uses a language model to process inputs and make use of external tools to gather information. In our example, the agent is set up to answer questions using a search tool (DuckDuckGo) and a language model (OpenAI's ChatGPT). DuckDuckGo is used here because it is simple to integrate and does not require API keys. We will later explore how the agent is created and how it interacts with the tools and the language model to produce responses."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "RXNmfAPJUiX5",
    "outputId": "81f56071-c3ff-4187-88bd-8d1c1d7deec4"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `duckduckgo_search` with `{'query': 'current value of the DJIA'}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[36;1m\u001b[1;3mDow Jones Industrial Average + Add to watchlist + Add an alert. DJI:DJI. Dow Jones Industrial Average. Actions. Add to watchlist; Add an alert; Price (USD) 39,411.21; Today's Change 260.88 / 0.67%; Shares traded 387.00m; ... Share price information may be rounded up/down and therefore not entirely accurate. Dow Jones Today: Get all information on the Dow Jones Index including historical chart, news and constituents. The Dow Jones Industrial Average ( DJINDICES:^DJI) is a stock index that tracks 30 of the largest U.S. companies. Created in 1896, it is one of the oldest stock indexes, and its performance is ... The observations for the Dow Jones Industrial Average represent the daily index value at market close. The market typically closes at 4 PM ET, except for holidays when it sometimes closes early. The Dow Jones Industrial Average provides a view of the US stock market and economy. Originally, the index was made up of 12 stocks, it now contains 30 ... Dow Jones Industrial Average - DJIA: The Dow Jones Industrial Average (DJIA) is a price-weighted average of 30 significant stocks traded on the New York Stock Exchange (NYSE) and the NASDAQ . The ...\u001b[0m\u001b[32;1m\u001b[1;3mThe current value of the Dow Jones Industrial Average (DJIA) is 39,411.21.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'What is the currnet value of the DJIA?',\n",
       " 'output': 'The current value of the Dow Jones Industrial Average (DJIA) is 39,411.21.'}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from re import VERBOSE\n",
    "from langchain import hub\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.agents import create_tool_calling_agent\n",
    "from langchain_community.tools import DuckDuckGoSearchRun\n",
    "from langchain.agents import AgentExecutor\n",
    "\n",
    "MODEL = 'gpt-4o-mini'\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "        model=MODEL,\n",
    "        temperature=0.2,\n",
    "        n=1\n",
    "    )\n",
    "\n",
    "search_tool = DuckDuckGoSearchRun()\n",
    "\n",
    "prompt = hub.pull(\"hwchase17/openai-functions-agent\")\n",
    "\n",
    "tools = [search_tool]\n",
    "agent = create_tool_calling_agent(llm, tools, prompt)\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n",
    "agent_executor.invoke({\"input\": \"What is the currnet value of the DJIA?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "KUsS8qUHO2oc"
   },
   "source": [
    "\n",
    "The code sets up a basic agent to answer questions by integrating a language model and a search tool. Initially, it imports necessary modules and initializes the language model (ChatOpenAI) with specified parameters such as model type and temperature. The DuckDuckGoSearchRun tool is instantiated for performing web searches. A prompt template is pulled from a repository to guide the agent's responses. The agent is then created using the create_tool_calling_agent function, which combines the language model, tools, and prompt. Input data is provided to test the agent with a question about the president of the United States. The AgentExecutor class is used to manage the agent's execution, allowing it to process further queries, such as checking the current value of the DJIA, with verbosity enabled to provide detailed output during execution. This setup demonstrates the integration of a language model with a search tool to perform information retrieval and question-answering tasks."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Fl9oeUkBPsIu"
   },
   "source": [
    "# Module 7 Assignment\n",
    "\n",
    "You can find the first assignment here: [assignment 7](https://github.com/jeffheaton/app_generative_ai/blob/main/assignments/assignment_yourname_class7.ipynb)"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3.11 (genai)",
   "language": "python",
   "name": "genai"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.8"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
